﻿namespace IronLua.Parsing.Ast

open System
open IronLua.Parsing   

type Symbol = string

type Expr =
    | Empty of TokenPosition
    | Nil of TokenPosition
    | Number of TokenPosition * double
    | String of TokenPosition * string
    | Boolean of TokenPosition * bool
    | ClrObj of TokenPosition * obj
    | Table of TokenPosition * Expr list
    | TableItem of TokenPosition * Expr
    | TableKeyValuePair of TokenPosition * Expr * Expr
    | TableValuePair of TokenPosition * Expr * Expr
    | VarArgs of TokenPosition
    | VarArgsExpr of TokenPosition
    | Identifier of TokenPosition * Symbol
    | Local of TokenPosition * int (* scope *) * int (* index *) * Symbol
    | Parameter of TokenPosition * int (* scope *) * int (* index *) * Symbol
    | Upvalue of TokenPosition * int (* scope *) * int (* depth *) * int (* index *) * Symbol
    | Assign of TokenPosition * Expr list * Expr list
    | Or of TokenPosition * Expr * Expr
    | And of TokenPosition * Expr * Expr
    | Lt of TokenPosition * Expr * Expr
    | Gt of TokenPosition * Expr * Expr
    | Lte of TokenPosition * Expr * Expr
    | Gte of TokenPosition * Expr * Expr    
    | Eq of TokenPosition * Expr * Expr
    | Neq of TokenPosition * Expr * Expr
    | Concat of TokenPosition * Expr * Expr
    | Add of TokenPosition * Expr * Expr
    | Sub of TokenPosition * Expr * Expr
    | Mul of TokenPosition * Expr * Expr
    | IntegerDiv of TokenPosition * Expr * Expr
    | Div of TokenPosition * Expr * Expr
    | Mod of TokenPosition * Expr * Expr
    | Not of TokenPosition * Expr
    | Unm of TokenPosition * Expr
    | Una of TokenPosition * Expr
    | Len of TokenPosition * Expr
    | Pow of TokenPosition * Expr * Expr
    | Label of TokenPosition * Symbol
    | Goto of TokenPosition * Symbol
    | Closure of TokenPosition * Guid * Expr list * Expr * (* upvaluecount: *) int * (* localcount: *) int * Map<Symbol, Expr>
    | Index of TokenPosition * Expr * Expr
    | Call of TokenPosition * Expr * Expr list
    | If of TokenPosition * Expr * Expr * Expr
    | For of TokenPosition * Expr * Expr list * Expr
    | GenericFor of TokenPosition * Expr list * Expr list * Expr
    | While of TokenPosition * Expr * Expr
    | Repeat of TokenPosition * Expr * Expr
    | Break of TokenPosition
    | Return of TokenPosition * Expr list
    | CoalesceResults of TokenPosition * Expr
    | LocalDecl of TokenPosition * Expr list * Expr list
    | LocalFunDecl of TokenPosition * Guid * Expr * Expr list * Expr * (* upvaluecount: *) int * (* localcount: *) int * Map<Symbol, Expr>
    | FunDecl of TokenPosition * Guid * Expr * Symbol * Expr list * Expr * (* upvaluecount: *) int * (* localcount: *) int * Map<Symbol, Expr>
    | Block of TokenPosition * Expr list
        member this.IsConstant 
            with get() = match this with
                         | Nil(_) 
                         | Number(_, _) 
                         | String(_, _) 
                         | Boolean(_, _) 
                         | ClrObj(_, _) ->  true
                         | _ ->             false
        member this.NeedsEvalBeforeAssign
            with get() = match this with
                         | Index(p, lhs, rhs) ->    not rhs.IsConstant
                         | _ ->                     false
        member this.EvalSubExpr
            with get() = if this.NeedsEvalBeforeAssign 
                         then match this with
                              | Index(p, lhs, rhs) -> Some(rhs)
                              | _ ->                  None   
                         else None
        member this.NeedsStorageSlot
            with get() = match this with
                         | Upvalue(_, _, _, _, _)
                         | Local(_, _, _, _)
                         | Parameter(_, _, _, _) -> true
                         | _ ->                     false
        member this.IsVarArgsFunc 
            with get() = let isvarargs expr = match expr with VarArgs(p) -> true | _ -> false
                         let hasvarargs parms =
                            match parms |> List.tryFind isvarargs with
                            | Some(_) ->    true
                            | None ->       false
                         match this with
                         | FunDecl(id, p, obj, n, parms, block, upn, locn, syms) -> hasvarargs parms
                         | LocalFunDecl(id, p, n, parms, block, upn, locn, syms) -> hasvarargs parms
                         | _ ->                                                     false

        member this.Position
            with get() = 
                match this with
                | Expr.Empty(pos)            
                | Expr.Nil(pos)              
                | Expr.Number(pos, _)        
                | Expr.String(pos, _)        
                | Expr.VarArgs(pos)    
                | Expr.VarArgsExpr(pos)    
                | Expr.Boolean(pos, _)      
                | Expr.Table(pos, _)
                | Expr.TableItem(pos, _) 
                | Expr.TableKeyValuePair(pos, _, _)
                | Expr.TableValuePair(pos, _, _)
                | Expr.Identifier(pos, _)    
                | Expr.Local(pos, _, _, _)
                | Expr.Parameter(pos, _, _, _)
                | Expr.Upvalue(pos, _, _, _, _)
                | Expr.ClrObj(pos, _)        
                | Expr.Assign(pos, _, _)     
                | Expr.Or(pos, _, _)         
                | Expr.And(pos, _, _)        
                | Expr.Lt(pos, _, _)         
                | Expr.Gt(pos, _, _)         
                | Expr.Lte(pos, _, _)        
                | Expr.Gte(pos, _, _)        
                | Expr.Eq(pos, _, _)         
                | Expr.Neq(pos, _, _)        
                | Expr.Concat(pos, _, _)     
                | Expr.Add(pos, _, _)        
                | Expr.Sub(pos, _, _)        
                | Expr.Mul(pos, _, _)        
                | Expr.Div(pos, _, _)        
                | Expr.IntegerDiv(pos, _, _)     
                | Expr.Mod(pos, _, _)        
                | Expr.Not(pos, _)           
                | Expr.Una(pos, _)           
                | Expr.Unm(pos, _)           
                | Expr.Len (pos, _)          
                | Expr.Pow(pos, _, _)       
                | Expr.Label(pos, _) 
                | Expr.Goto(pos, _)
                | Expr.Closure(pos, _, _, _, _, _, _)
                | Expr.Index(pos, _, _) 
                | Expr.Call(pos, _, _)
                | Expr.CoalesceResults(pos, _)
                | Expr.Call(pos, _, _) 
                | Expr.If(pos, _, _, _)
                | Expr.For(pos, _, _, _)
                | Expr.GenericFor(pos, _, _, _)
                | While(pos, _, _) 
                | Repeat(pos, _, _)
                | Break(pos) 
                | Return(pos, _)
                | Expr.LocalDecl(pos, _, _)
                | Expr.LocalFunDecl(pos, _, _, _, _, _, _, _)
                | Expr.FunDecl(pos, _, _, _, _, _, _, _, _)
                | Expr.Block(pos, _) ->                      pos

        member this.Description
            with get() = 
                match this with 
                | Expr.Empty(_) ->          "none"
                | Expr.Nil(_) ->            "nil"
                | Expr.Number(_, _) ->      "number"
                | Expr.String(_, _) ->      "string"
                | Expr.VarArgs(_) ->        "varargs"
                | Expr.VarArgsExpr(_) ->    "varargs expression"
                | Expr.Boolean(_, _) ->     "boolean"
                | Expr.Table(_, _) ->       "table"
                | Expr.TableItem(_, _) ->   "table item"
                | Expr.TableKeyValuePair(_, _, _) -> "table key-value pair"
                | Expr.TableValuePair(_, _, _) -> "table value pair"
                | Expr.Identifier(_, _) ->  "identifier"
                | Expr.Local(_, _, _, _) ->  "local"
                | Expr.Parameter(_, _, _, _) -> "parameter"
                | Expr.Upvalue(_, _, _, _, _) -> "upvalue"
                | Expr.ClrObj(_, _) ->      "CLR Object"
                | Expr.Assign(_, _, _) ->   "="
                | Expr.Or(_, _, _) ->       "or"
                | Expr.And(_, _, _) ->      "and"
                | Expr.Lt(_, _, _) ->       "<"
                | Expr.Gt(_, _, _) ->       ">"
                | Expr.Lte(_, _, _) ->      "<="
                | Expr.Gte(_, _, _) ->      ">="
                | Expr.Eq(_, _, _) ->       "=="
                | Expr.Neq(_, _, _) ->      "~="
                | Expr.Concat(_, _, _) ->   ".."   
                | Expr.Add(_, _, _) ->      "+"
                | Expr.Sub(_, _, _) ->      "-"
                | Expr.Mul(_, _, _) ->      "*"
                | Expr.Div(_, _, _) ->      "/"
                | Expr.IntegerDiv(_, _, _) -> "//"
                | Expr.Mod(_, _, _) ->      "%"
                | Expr.Not(_, _) ->         "not"
                | Expr.Una(_, _) ->         "+"
                | Expr.Unm(_, _) ->         "-"
                | Expr.Len (_, _) ->        "#"
                | Expr.Pow(_, _, _) ->      "^"    
                | Expr.Label(_, _) ->       "label"
                | Expr.Goto(_, _) ->        "goto"
                | Expr.Closure(_, _, _, _, _, _, _) ->  "closure"
                | Expr.Index(_, _, _) ->    "indexer"
                | Expr.Call(_, _, _) ->     "function call"
                | Expr.If(_, _, _, _) ->    "if"
                | Expr.For(_, _, _, _) ->   "for"
                | Expr.GenericFor(_, _, _, _) ->   "generic for"
                | While(_, _, _) ->         "while"
                | Repeat(_, _, _) ->        "repeat"
                | Break(_) ->               "break"
                | Return(_, _) ->           "return"
                | Expr.CoalesceResults(_, _) -> "result coalesce"
                | Expr.LocalDecl(_, _, _) -> "local declaration"
                | Expr.LocalFunDecl(_, _, _, _, _, _, _, _) -> "local function declaration"
                | Expr.FunDecl(_, _, _, _, _, _, _, _, _) ->   "function declaration"
                | Expr.Block(_, _) ->       "block"
